﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;

namespace Borg.Framework
{
    public class CompositionSaver : CompositionPersistor, IDisposable
    {
        private readonly string _description;
        private readonly ExtensionInfo _extension;
        private readonly IList<SlotInfo> _slotsToSave;
        private readonly StreamWriter _writer;

        private CompositionSaver(ExtensionInfo ext, IEnumerable<SlotInfo> slots, string description, string filename)
        {
            _extension = ext;
            _description = description;
            _writer = new StreamWriter(
                new IsolatedStorageFileStream(filename, FileMode.Create, store));
            _slotsToSave = new List<SlotInfo>(slots);
        }

        public void Dispose()
        {
            _writer.Dispose();
        }

        private void Save()
        {
            try
            {
                _writer.WriteLine("Borg Compilation Version 0.1.3.0");
                _writer.WriteLine(_description);

                if (!_extension.IsPlugged)
                    return;

                _writer.Write("ROOT;{0}", _extension.IsShared ? "S" : "U");
                WriteExtensionType(_extension.ExtensionTypeInfo);
                SaveSlots();
            }
            finally
            {
                _writer.Close();
            }
        }

        public static void Save(ExtensionInfo ext, string description)
        {
            Save(ext, ext.SlotInfos, description);
        }

        public static void Save(ExtensionInfo ext, IEnumerable<SlotInfo> slots, string description)
        {
            using (var saver = new CompositionSaver(
                ext, slots, description, System.IO.Path.GetRandomFileName()))
                saver.Save();
        }

        private void SavePluggedPlugs(SlotInfo slot)
        {
            foreach (var pi in slot.PluggedPlugInfos)
            {
                _writer.Write("EXT P;{0}", pi.ExtensionInfo.IsShared ? "S" : "U");
                WriteExtensionType(pi.PlugTypeInfo.ExtensionTypeInfo);
                
                foreach (var si in SlotOpenHistory.
                    GetOpenHistory(pi.ExtensionInfo).Where(si => si.IsOpen))
                {
                    WriteSlotProperties(si);
                    _slotsToSave.Add(si);
                }
            }
        }

        private void SaveRegisteredPlugs(SlotInfo slot)
        {
            foreach (var pti in slot.RegisteredPlugTypeInfos)
            {
                _writer.Write("EXT R;");
                WriteExtensionType(pti.ExtensionTypeInfo);
            }
        }

        private void SaveSelectedPlugs(SlotInfo slot)
        {
            foreach (var pi in slot.SelectedPlugs)
            {
                _writer.Write("EXT S;");
                WriteExtensionType(pi.PlugTypeInfo.ExtensionTypeInfo);
            }
        }

        private void SaveSlots()
        {
            SlotInfo si;

            using (var enumerator = _slotsToSave.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    si = enumerator.Current;

                    if (si.IsOpen)
                        WriteSlotProperties(si);
                }
            }

            while (_slotsToSave.Count > 0)
            {
                si = _slotsToSave[0];

                _slotsToSave.RemoveAt(0);
                _writer.WriteLine();
                
                SaveRegisteredPlugs(si);
                SavePluggedPlugs(si);
                SaveSelectedPlugs(si);
            }
        }

        private void WriteExtensionType(ExtensionTypeInfo type)
        {
            _writer.Write(";{0};{1};{2};{3}",
                new object[]
                    {
                        type.Name,
                        type.PluginInfo.Name,
                        type.PluginInfo.RepositoryId.Uri,
                        type.PluginInfo.RepositoryId.LastWriteTime
                    });
        }

        private void WriteSlotProperties(SlotInfo slot)
        {
            _writer.Write("SLOT AUTOOPEN={0};AUTOPLUG={1};AUTOREGISTER={2};AUTORELEASE={3};LAZYLOAD={4};UNIQUE={5};SELECTIONMODE={6};{7}",
                new object[]
                    {
                        slot.AutoOpen ? 't' : 'f',
                        slot.AutoPlug ? 't' : 'f',
                        slot.AutoRegister ? 't' : 'f',
                        slot.AutoRelease ? 't' : 'f',
                        slot.LazyLoad ? 't' : 'f',
                        slot.Unique ? 't' : 'f',
                        slot.SelectionMode,
                        slot.Name
                    });
        }
    }
}

